package com.longqin.business.util;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisListCommands;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.connection.RedisZSetCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import com.google.gson.Gson;

@Component
public class RedisUtil {

	@Resource(name="redisTemplate")
	private RedisTemplate<String, Object> redisTemplate;
	
	/*** 字符串类型redis命令封装 ******/
	/**
	 * 基本数据类型赋值
	 * 
	 * @param key
	 *            键
	 * @param value
	 *            值
	 */
	public boolean setKeyValue(String key, Object value) {
		try {
			redisTemplate.opsForValue().set(key, value);
			return true;
		} catch (Exception e) {
			return false;
		}
	}
	
	/**删除key
	 * @param key
	 * @return
	 */
	public boolean deleteKeyValue(String key) {
		try {
			redisTemplate.delete(key);
			return true;
		} catch (Exception e) {
			return false;
		}
	}

	

	/**基本数据类型赋值
	 * @param key 键
	 * @param value 值
	 * @param timeout 超时时间
	 * @param timeType 时间类型：时、分、秒
	 * @return
	 */
	public boolean setKeyValue(String key, Object value,int timeout,TimeUnit timeType) {
		try {
			redisTemplate.opsForValue().set(key, value);
			redisTemplate.expire(key, timeout, timeType);
			return true;
		} catch (Exception e) {
			return false;
		}
	}
	/**
	 * 基本数据获取值
	 * 
	 * @param key
	 * @return
	 */
	public Object getValueByKey(String key) {
		return key == null ? null : redisTemplate.opsForValue().get(key);
	}

	/**
	 * 递增key(实现id的递增、统计文章的访问量等)
	 * 
	 * @param key
	 * @param delta 歩长
	 * @param timeout 超时时间
	 * @param timeType 时间类型     
	 * @return 
	 */
	public long incrKey(String key, long delta, long timeout, TimeUnit timeType) {
		long num = redisTemplate.opsForValue().increment(key, delta);
		if(num==1)redisTemplate.expire(key, timeout, timeType);//第一次设置超时时间为一天
		return num;
	}
	
	/**
	 * 递增key(实现id的递增、统计文章的访问量等)
	 * 
	 * @param key
	 * @param delta
	 *            歩长
	 * @return
	 */
	public long  incrKey(String key, long delta) {
		return redisTemplate.opsForValue().increment(key, delta);
	}

	/**
	 * 递减
	 * 
	 * @param key
	 *            键
	 * @param by
	 *            要减少几(小于0)
	 * @return
	 */
	public long decr(String key, long delta) {
		if (delta < 0) {
			throw new RuntimeException("递减因子必须大于0");
		}
		return redisTemplate.opsForValue().increment(key, -delta);
	}

	/**
	 * 向尾部追加值
	 * 
	 * @param key
	 * @param append
	 *            追加的值
	 */
	public void appendKeyValue(String key, String append) {
		redisTemplate.opsForValue().append(key, append);
	}

	/**
	 * 位操作，根据偏移量获取值
	 * 
	 * @param key
	 * @param offset
	 * @return true：当前二进制位是1，false：0 如果需要获取的二进制位的索引超出了键值的二进制位的实际长度则默认位值是0：
	 */
	public boolean getBit(String key, Long offset) {
		return redisTemplate.opsForValue().getBit(key, offset);
	}

	/**
	 * 位操作，根据偏移量设置值
	 * 
	 * @param key
	 * @param offset
	 * @param value
	 * @return如果要设置的位置超过了键值的二进制位的长度，SETBIT命令会自动将中间的二进制位 设置为0，同理设置一个不存在的键的指定二进制位的值会自动将其前面的位赋值为0
	 */
	public boolean setBit(String key, Long offset, boolean value) {
		return redisTemplate.opsForValue().setBit(key, offset, value);
	}

	/**BITCOUNT命令可以获得字符串类型键中值是1的二进制位个数
	 * @param key
	 * @return 
	 */
	public long bitCount(String key) {
	    return redisTemplate.execute((RedisCallback<Long>) con -> con.bitCount(key.getBytes()));
	}
	
	/**可以通过参数来限制统计的字节范围，如我们只希望统计前两个字节
	 * bitCountRange key(值为aar) 0 1（即"aa"）：6
	 * @param key
	 * @param start
	 * @param end
	 * @return
	 */
	public long bitCountRange(String key,long start,long end) {
	    return redisTemplate.execute((RedisCallback<Long>) con -> con.bitCount(key.getBytes(), start, end));
	}
	
	/**BITOP命令可以对多个字符串类型键进行位运算，并将结果存储在destkey参数指定的键
中。BITOP命令支持的运算操作有AND、OR、XOR 和NOT
	 * @param op :AND, OR, XOR, NOT;
	 * @param saveKey
	 * @param desKey
	 * @return
	 */
	public Long bitOp(RedisStringCommands.BitOperation op, String saveKey, String... desKey) {
	    byte[][] bytes = new byte[desKey.length][];
	    for (int i = 0; i < desKey.length; i++) {
	        bytes[i] = desKey[i].getBytes();
	    }
	    return redisTemplate.execute((RedisCallback<Long>) con -> con.bitOp(op, saveKey.getBytes(), bytes));
	}
	
	/****************************字符串类型命令结束*******************************/
	
	/****************************散列类型命令开始********************************/
	
	/**散列根据key插入某个属性的值
	 * @param key 键
	 * @param field 属性
	 * @param value 值
	 * @return 当执行的是插入操作时（即之前字段不存在）HSET命令会返回1，当执行的是更新操作时（即之前字段已经存在）HSET命令会返回0
	 */
	public void hSet(String key,Object field,Object value){
			//putIfAbsent命令存在则返回不覆盖
		 redisTemplate.opsForHash().put(key, field, value);
	}
	
	
	/**散列根据key获取某个属性的值
	 * @param key
	 * @param field
	 * @param value
	 * @return
	 */
	public Object hGet(String key,Object field){
		return redisTemplate.opsForHash().get(key,field);
	}
	
	/**散列设置map:多属性多值
	 * @param key
	 * @param dataMap
	 */
	public void hMSet(String key, Map<String, Object> dataMap){
		redisTemplate.opsForHash().putAll(key,dataMap);
	}
	
	/**获取散列多个传入的属性的值
	 * @param key
	 * @param hashKeys
	 */
	public void hMGet(String key, Collection<Object> hashKeys){
		redisTemplate.opsForHash().multiGet(key, hashKeys);
	}
	
	
	/**根据key来获取散列
	 * @param key
	 * @return
	 * @author julong
	 * @date 2017-12-8 上午10:24:03
	 */
	public Map<Object, Object> hGetAll(String key) {
		return this.redisTemplate.opsForHash().entries(key);
	}
	
	
	/**判断散列中该字段是否存在
	 * @param key redis散列的key
	 * @param hashKey map的key
	 * @return
	 */
	public boolean hExists(String key,Object hashKey) {
		return this.redisTemplate.opsForHash().hasKey(key, hashKey);
	}
	
	/**删除散列的属性（一个或者多个）
	 * @param key
	 * @param mapKey
	 * @return 返回值是被删除的字段个数
	 */
	public long hDel(String key,Object... mapKey){
		 return redisTemplate.opsForHash().delete(key, mapKey);
	}
	
	/**获取散列的key集合
	 * @param key
	 * @return
	 */
	public Set<Object>  hKeys(String key){
		return redisTemplate.opsForHash().keys(key);
	}
	
	/**HVALS命令与HKEYS命令相对应，HVALS命令用来获得键中所有字段值
	 * @param key
	 * @return
	 */
	public List<Object>  hVals(String key){
		return redisTemplate.opsForHash().values(key);
	}
	
	
	/**获得字段数量
	 * @param key
	 * @return
	 */
	public long  hLen(String key){
		return redisTemplate.opsForHash().size(key);
	}
	
	/*******************************散列类型命令结束******************************/
	
	
	/*******************************列表类型命令开始******************************/
	/**向列表左插入一个元素
	 * @param key
	 * @param value
	 * @return 返回的是列表的长度
	 */
	public long lPush(String key,Object value){
		return redisTemplate.opsForList().leftPush(key, value);
	}
	
	/**向列表右插入一个元素
	 * @param key
	 * @param value
	 * @return 返回的是列表的长度
	 */
	public long rPush(String key,Object value){
		return redisTemplate.opsForList().rightPush(key, value);
	}
	
	/**向列表左插入多个元素
	 * @param key
	 * @param values
	 * @return 返回的是列表的长度
	 */
	public long lPushAll(String key,Collection<Object> values){
		return redisTemplate.opsForList().leftPushAll(key, values);
	}
	
	/**向列表右插入多个元素
	 * @param key
	 * @param values
	 * @return 返回的是列表的长度
	 */
	public long rPushAll(String key,Collection<Object> values){
		return redisTemplate.opsForList().rightPushAll(key, values);
	}
	
	
	/**向列表左移除一个元素
	 * @param key
	 * @param value
	 * @return 返回被移除的元素值
	 */
	public Object lPop(String key){
		return redisTemplate.opsForList().leftPop(key);
	}
	
	/**向列表右移除一个元素
	 * @param key
	 * @param value
	 * @return 返回被移除的元素值
	 */
	public Object rPop(String key){
		return redisTemplate.opsForList().rightPop(key);
	}
	
	/**获取列表中元素的个数
	 * @param key
	 * @return 当键不存在时LLEN会返回0
	 */
	public long lLen(String key){
		return redisTemplate.opsForList().size(key);
	}
	
	/**获得列表中的某一片段
	 * @param key
	 * @param start
	 * @param end 
	 * @return (1)返回索引从start到stop之间的所有元素（包含两端的元素）,起始索引为0
	 * (2)LRANGE命令也支持负索引，表示从右边开始计算序数，如 LRANGE key -2 -1:"-1"表示最右边第一个元
	 * 素，"-2"表示最右边第二个元素，依次类推
	 * (3) 显然，LRANGE numbers 0 -1可以获取列表中的所有元素。另外一些特殊情况如下。
	 *****（a）如果start的索引位置比stop的索引位置靠后，则会返回空列表。
	 *****（b）如果stop大于实际的索引范围，则会返回到列表最右边的元素：
	 */
	public List<Object> lRange(String key,long start,long end){
		 return redisTemplate.opsForList().range(key, start, end);
	}
	
	/**LREM命令会删除列表中前count个值为value的元素，返回值是实际删除的元素个数。根
	 *据count值的不同，LREM命令的执行方式会略有差异：
	 *●当count＞0时LREM命令会从列表左边开始删除前count个值为value的元素；
	 *●当count＜0时LREM 命令会从列表右边开始删除前|count|个值为value的元素；
	 *●当count=0是LREM命令会删除所有值为value的元素
	 * @param key
	 * @param count
	 * @param value
	 * @return 返回值是实际删除的元素个数
	 */
	public long lRem(String key,long count,Object value){
		 return redisTemplate.opsForList().remove(key, count, value);
	}
	
	/**将列表类型当作数组来用
	 * @param key
	 * @param index 索引
	 * @return 索引返回的元素，索引从0开始；如果index是负数则表示从右边开始计算的索引，最右边元素的索引是-1
	 */
	public Object lIndex(String key,long index){
		 return redisTemplate.opsForList().index(key, index);
	}
	
	/** LSET是另一个通过索引操作列表的命令，它会将索引为index的元素赋值为value
	 * @param key
	 * @param index
	 * @param value
	 */
	public void lSet(String key,long index,Object value){
		 redisTemplate.opsForList().set(key, index, value);
	}
	
	/**LTRIM命令可以删除指定索引范围之外的所有元素，其指定列表范围的方法和LRANGE命令相同
	 * @param key
	 * @param start
	 * @param end
	 */
	public void lTrim(String key,long start,long end){
		redisTemplate.opsForList().trim(key, start, end);
	}
	
	/**LINSERT命令首先会在列表中从左到右查找值为pivot的元素，然后根据第二个参数是
	 *BEFORE还是AFTER来决定将value插入到该元素的前面还是后面
	 * @param key
	 * @param where
	 * @param pivot
	 * @param value
	 * @return 返回值是插入后列表的元素个数
	 */
	public long lInsert(String key,RedisListCommands.Position where,Object pivot,Object value){
		return redisTemplate.execute(new RedisCallback<Long>() {
			@Override
			public Long doInRedis(RedisConnection connection) throws DataAccessException {
				Gson gson=new Gson();
				return connection.lInsert(key.getBytes(), where, gson.toJson(pivot).getBytes(), gson.toJson(value).getBytes());
			}
		});
	}
	
	/**RPOPLPUSH命令会先从source列表类型键的右边弹出一个元素，然后将其加入
		到destination列表类型键的左边，并返回这个元素的值，整个过程是原子的
	 * @param sourceKey
	 * @param destinationKey
	 * @return 返回source右边弹出元素的值
	 */
	public Object popLPush(String sourceKey,String destinationKey){
		return redisTemplate.opsForList().rightPopAndLeftPush(sourceKey, destinationKey);
	}
	
	/*******************************列表类型命令结束******************************/
	
	/*******************************集合类型命令开始******************************/
	/**SADD命令用来向集合中增加一个或多个元素
	 * @param key
	 * @param values
	 * @param return 返回值是成功加入的元素数量
	 */
	public long sAdd(String key,Object... values){
		return redisTemplate.opsForSet().add(key, values);
	}
	
	/**SREM命令用来从集合中删除一个或多个元素
	 * @param key
	 * @param values
	 * @return 返回删除成功的个数
	 */
	public long sRem(String key,Object... values){
		return redisTemplate.opsForSet().remove(key, values);
	}
	
	/**SMEMBERS命令会返回集合中的所有元素
	 * @param key
	 * @return
	 */
	public Set<Object> sMembers(String key){
		return redisTemplate.opsForSet().members(key);
	}
	
	/**判断一个元素是否在集合中，无论集合中有多少个元素，SISMEMBER命令始终可以极快地返回结果
	 * @param key
	 * @param value
	 * @return
	 */
	public boolean sisMember(String key,Object value){
		return redisTemplate.opsForSet().isMember(key, value);
	}
	
	/**SDIFF命令用来对多个集合执行差集运算。集合A与集合B的差集表示为A-B
	 * @param key
	 * @param otherKeys 其他key，一个或多个
	 * return 返回差集元素
	 */
	public  Set<Object> sDiff(String key,Collection<String> otherKeys){
		return redisTemplate.opsForSet().difference(key, otherKeys);
	}
	
	/**SINTER命令用来对多个集合执行交集运算
	 * @param key
	 * @param otherKeys
	 * @return
	 */
	public  Set<Object> sInter(String key,Collection<String> otherKeys){
		return redisTemplate.opsForSet().intersect(key, otherKeys);
	}
	
	/**SUNION命令用来对多个集合执行并集运算
	 * @param key
	 * @param otherKeys
	 * @return
	 */
	public  Set<Object> sUnion(String key,Collection<String> otherKeys){
		return redisTemplate.opsForSet().union(key, otherKeys);
	}
	
	
	/**SCARD命令用来获得集合中的元素个数
	 * @param key
	 * @return
	 */
	public long sCard(String key){
		return redisTemplate.opsForSet().size(key);
	}
	
	/**SDIFFSTORE命令和SDIFF命令功能一样，唯一的区别就是前者不会直接返回运算结果，而是将结果存储在destination键中
	 * @param key
	 * @param otherKeys 其他key，一个或多个
	 * return 
	 */
	public long sDiffStore(String key,Collection<String> otherKeys,String destKey){
		return redisTemplate.opsForSet().differenceAndStore(key, otherKeys, destKey);
	}
	
	/**SINTERSTORE命令用来对多个集合执行交集运算,将结果存储在destination键中
	 * @param key
	 * @param otherKeys
	 * @return
	 */
	public  long sInterStore(String key,Collection<String> otherKeys,String destKey){
		return redisTemplate.opsForSet().intersectAndStore(key, otherKeys, destKey);
	}
	
	/**sUnionStore命令用来对多个集合执行并集运算,将结果存储在destination键中
	 * @param key
	 * @param otherKeys
	 * @return
	 */
	public  long sUnionStore(String key,Collection<String> otherKeys,String destKey){
		return redisTemplate.opsForSet().unionAndStore(key, otherKeys, destKey);
	}
	
	/**SRANDMEMBER命令用来随机从集合中获取多个元素，可以重复
	 * @param key
	 * @param count
	 * @return
	 */
	public  List<Object> sRandMember(String key,long count){
		return redisTemplate.opsForSet().randomMembers(key, count);
	}
	
	/**SPOP命令会从集合中随机选择多个元素弹出。
	 * @param key
	 * @param count
	 * @return
	 */
	public  List<Object> sPop(String key,long count){
		return redisTemplate.opsForSet().pop(key, count);
	}
	
	/*******************************集合类型命令结束******************************/
	
	/*******************************有序集合类型命令结束***************************/
	/**ZADD命令用来向有序集合中加入一个元素和该元素的分数，如果该元素已经存在则会用新的分数替换原有的分数
	 * @param key
	 * @param tuples
	 * @return 返回值是新加入到集合中的元素个数（不包含之前已经存在的元素）
	 */
	public boolean zAdd(String key,Object value,double score){
		return redisTemplate.opsForZSet().add(key, value, score);
	}
	
	/*public long zAddAll(String key,Set<TypedTuple<?>> tuples){
		return redisTemplate.opsForZSet().add(key, tuples);
	}*/
	
	/**获得元素的分数
	 * @param key
	 * @param value
	 */
	public double zScore(String key,Object value){
		 return redisTemplate.opsForZSet().score(key, value);
	}
	
	/**ZRANGE命令会按照元素分数从小到大的顺序返回索引从start到stop之间的所有元素
	 * 索引都是从0开始，负数代表从后向前查找（-1表示最后一个元素）;rangeWithScores命令带上分数
	 * @param key
	 * @param start
	 * @param end
	 * @return 
	 */
	public Set<Object> zRange(String key,long start,long end){
		return redisTemplate.opsForZSet().range(key, start, end);
	}
	
	/**ZREVRANGE命令是按照元素分数从大到小的顺序给出结果的
	 * @param key
	 * @param start
	 * @param end
	 * @return
	 */
	public Set<Object> zRevRange(String key,long start,long end){
		return redisTemplate.opsForZSet().reverseRange(key, start, end);
	}
	
	/**该命令按照元素分数从小到大的顺序返回分数在min和max之间（包含min和max）的元素
	 * @param key
	 * @param min
	 * @param max
	 * @return
	 */
	public Set<Object> zRangeByScore(String key,double min,double max){
		return redisTemplate.opsForZSet().rangeByScore(key, min, max);
	}
	
	/**该命令按照元素分数从大到小的顺序返回分数在min和max之间（包含min和max）的元素
	 * @param key
	 * @param min
	 * @param max
	 * @return
	 */
	public Set<Object> zRevRangeByScore(String key,double min,double max){
		return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max);
	}
	
	/**该命令按照元素分数从小到大的顺序返回分数在min和max之间（包含min和max）指定位置的元素
	 * @param key
	 * @param min
	 * @param max
	 * @param offset 偏移量
	 * @param count 返回的个数
	 * @return
	 */
	public Set<Object> zRangeByScore(String key,double min,double max,long offset,long count){
		return redisTemplate.opsForZSet().rangeByScore(key, min, max, offset, count);
	}
	
	/**该命令按照元素分数从大到小的顺序返回分数在min和max之间（包含min和max）指定位置的元素
	 * @param key
	 * @param min
	 * @param max
	 * @param offset 偏移量
	 * @param count 返回的个数
	 * @return
	 */
	public Set<Object> zRevRangeByScore(String key,double min,double max,long offset,long count){
		return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max, offset, count);
	}
	
	/**ZINCRBY命令可以增加一个元素的分数
	 * @param key
	 * @param value 元素
	 * @param delta 增加的分数，负数为减分
	 * @return 返回增加后的分数
	 */
	public double zIncrby(String key,Object value,double delta){
		return redisTemplate.opsForZSet().incrementScore(key, value, delta);
	}
	
	
	/**获得集合中元素的数量
	 * @param key
	 * @return
	 */
	public long zCard(String key){
		return redisTemplate.opsForZSet().size(key);
	}
	
	/**获得指定分数范围內的元素个数
	 * @param key
	 * @param min
	 * @param max
	 * @return
	 */
	public long zCount(String key,double min,double max){
		return redisTemplate.opsForZSet().count(key, min, max);
	}
	
	/**删除一个或多个元素
	 * @param key
	 * @param values
	 * @return 返回值是成功删除的元素数量（不包含本来就不存在的元素）
	 */
	public long zRem(String key,Object... values){
		return redisTemplate.opsForZSet().remove(key, values);
	}
	
	/**ZREMRANGEBYRANK命令按照元素分数从小到大的顺序（即索引0表示最小的值）删除
	 *处在指定排名范围内的所有元素
	 * @param key
	 * @param start
	 * @param end
	 * @return 返回删除的元素数量
	 */
	public long zRemRangeByRank(String key,long start,long end){
		return redisTemplate.opsForZSet().removeRange(key, start, end);
	}
	
	/**ZREMRANGEBYSCORE命令会删除指定分数范围内的所有元素
	 * @param key
	 * @param min
	 * @param max
	 * @return 返回删除的元素数量
	 */
	public long zRemRangeByScore(String key,double min,double max){
		return redisTemplate.opsForZSet().removeRangeByScore(key, min, max);
	}
	
	/**ZRANK命令会按照元素分数从小到大的顺序获得指定的元素的排名（从0开始，即分数最
	 *小的元素排名为0
	 * @param key
	 * @param value
	 * @return
	 */
	public long zRank(String key,Object value){
		return redisTemplate.opsForZSet().rank(key, value);
	}
	
	/**ZREVRANK命令则相反（分数最大的元素排名为0）
	 * @param key
	 * @param value
	 * @return
	 */
	public long zRevRank(String key,Object value){
		return redisTemplate.opsForZSet().reverseRank(key, value);
	}
	
	/**ZINTERSTORE命令用来计算多个有序集合的交集并将结果存储在destination键中（同样
	 *以有序集合类型存储），返回值为destination键中的元素个数
	 * @param key
	 * @param otherKeys
	 * @param destKey
	 * @param aggregate 
	  			SUM：集合中该元素分数的和；
	  			MIN:destination键中元素的分数是每个参与计算的集合中该元素分数的最小值
	  			MAX:destination键中元素的分数是每个参与计算的集合中该元素分数的最大值
	 * @return
	 */
	public long zInterStore(String key,Collection<String> otherKeys,
				String destKey,RedisZSetCommands.Aggregate aggregate){
		return redisTemplate.opsForZSet().intersectAndStore(key, otherKeys, destKey,aggregate);
	}
	
	/**计算多个有序集合的并集并将结果存储在destination键中（同样
	 *以有序集合类型存储），返回值为destination键中的元素个数
	 * @param key
	 * @param otherKeys
	 * @param destKey
	 * @param aggregate
	 * @return
	 */
	public long zUnionStore(String key,Collection<String> otherKeys,
			String destKey,RedisZSetCommands.Aggregate aggregate){
		return redisTemplate.opsForZSet().unionAndStore(key, otherKeys, destKey, aggregate);
	}
	
	/*******************************有序集合类型命令结束***************************/
	
	/*******************************分布式锁及分布式id***************************/
    private Logger logger = LoggerFactory.getLogger(getClass());
	
	//加锁lua脚本
	private String lock = "if redis.call(\"setnx\",KEYS[1],ARGV[1]) == 1 "
						+ " then return redis.call(\"expire\",KEYS[1],ARGV[2])"
						+ " else return 0 end";

	//解锁lua脚本
	private String unLock = "if redis.call(\"get\",KEYS[1]) == ARGV[1] "
						+ " then return redis.call(\"del\",KEYS[1])"
						+ " else return 0 "
						+ " end";
	//步长默认为1
	private int delta = 1;
	
	@Resource(name="lockRedisTemplate")
	private RedisTemplate<String, Object> lockRedisTemplate;
	
	//获取分布式自增id
	public int generateId(String key) {
		String requestId = UUID.randomUUID().toString();
		int seq = 1;
		boolean isReleaseLock = true;//是否释放锁
		String lockKey = key + "_lock";
		try {
			boolean mLock = lock(requestId, lockKey, 1*10);//锁的有效时间10秒
			if(mLock){
				isReleaseLock = false;
				seq = (int)incrKey(key, delta);
				releaseLock(requestId, lockKey);//释放锁
				isReleaseLock = true;
			}
		} catch (Exception e) {
			logger.error( "获取自增id失败",e);
		}finally{
			if(!isReleaseLock){
				releaseLock(requestId, lockKey);
			}
		}
		return seq;
	}
	
	public boolean lock(String requestId, String key, int expiresTime) {
		try {
			while(true){
				DefaultRedisScript<Long> defaultRedisScript = new DefaultRedisScript<Long>(lock, Long.class);
				Long result = lockRedisTemplate.execute(defaultRedisScript,Collections.singletonList(key), requestId, String.valueOf(expiresTime));
				if(null != result && result == 1){
					return true;	
				} 
				Thread.sleep(40);
			}
		} catch (InterruptedException e) {
			logger.info("获取分布式锁失败");
			return false;
		}
	}
	
	public boolean getLock(String requestId, String key, int expiresTime) {
		DefaultRedisScript<Long> defaultRedisScript = new DefaultRedisScript<Long>(lock, Long.class);
		Long result = lockRedisTemplate.execute(defaultRedisScript,Collections.singletonList(key), requestId, String.valueOf(expiresTime));
		if(null != result && result == 1){
			return true;	
		} else {
			return false;
		}
	}

	public boolean releaseLock(String requestId, String key) {
		DefaultRedisScript<Long> defaultRedisScript = new DefaultRedisScript<Long>(unLock, Long.class);
		long result = lockRedisTemplate.execute(defaultRedisScript,Collections.singletonList(key), requestId);
		return result == 1;
	}
	
	/*******************************分布式锁及分布式id结束***************************/
	
}
